# -*- coding: UTF-8 -*-
from django.conf import settings
from django.core.cache import cache
from django.utils.httpwrappers import HttpResponseNotModified
from django.utils.text import compress_string
import datetime, md5

class CacheMiddleware:
    """
    Le module intermédiaire pour le cache. Si ce module intermédiaire est 
    activé, chaque page générée par Django sera mise en cache pendant 
    CACHE_MIDDLEWARE_SECONDS secondes. Les clés du cache sont générées à partir 
    des URL des pages. Les pages possédant des paramètres GET ou POST ne sont 
    pas mises en cache.

    Si le cache est partagé entre plusieurs sites utilisant tous la même 
    installation Django, définissez le paramètre CACHE_MIDDLEWARE_KEY_PREFIX 
    comme étant le nom du site ou une autre chaîne caractérisant de manière 
    unique votre instance de Django, afin d'éviter que les clés du cache 
    générées pour les différents sites n'entrent en collision.

    Ce module intermédiaire réalisera aussi les optimisations suivantes :

    * Si le paramètre CACHE_MIDDLEWARE_GZIP vaut True, le contenu du site sera 
      compressé grâce à gzip.

    * Des balises ETag seront ajoutées; elles utiliseront un hash MD5 simple 
      obtenu à partir du contenu de la page.
    """
    def process_request(self, request):
        """
        Vérifie si la page a déjà été mise en cache. Si c'est le cas, renvoie la 
        version mise en cache. Gère aussi les trucs liés aux balises ETag.
        """
        if request.GET or request.POST:
            request._cache_middleware_set_cache = False
            return None # On ne se donne même pas la peine de vérifier le cache.

        accept_encoding = ''
        if settings.CACHE_MIDDLEWARE_GZIP:
            try:
                accept_encoding = request.META['HTTP_ACCEPT_ENCODING']
            except KeyError:
                pass
        accepts_gzip = 'gzip' in accept_encoding
        request._cache_middleware_accepts_gzip = accepts_gzip

        # On utilise la même clé pour le cache que le décorateur 
        # views.decorators.cache.cache_page, afin que le cache soit partagé.
        cache_key = 'views.decorators.cache.cache_page.%s.%s.%s' % \
            (settings.CACHE_MIDDLEWARE_KEY_PREFIX, request.path, accepts_gzip)
        request._cache_middleware_key = cache_key

        response = cache.get(cache_key, None)
        if response is None:
            request._cache_middleware_set_cache = True
            return None
        else:
            request._cache_middleware_set_cache = False
            # La logique du code qui suit provient de 
            # http://simon.incutio.com/archive/2003/04/23/conditionalGet
            try:
                if_none_match = request.META['HTTP_IF_NONE_MATCH']
            except KeyError:
                if_none_match = None
            try:
                if_modified_since = request.META['HTTP_IF_MODIFIED_SINCE']
            except KeyError:
                if_modified_since = None
            if if_none_match is None and if_modified_since is None:
                pass
            elif if_none_match is not None and response['ETag'] != if_none_match:
                pass
            elif if_modified_since is not None and response['Last-Modified'] != if_modified_since:
                pass
            else:
                return HttpResponseNotModified()
        return response

    def process_response(self, request, response):
        """
        Ajoute, si nécessaire, une clé au cache.
        """
        if request._cache_middleware_set_cache:
            content = response.get_content_as_string('utf-8')
            if request._cache_middleware_accepts_gzip:
                content = compress_string(content)
                response.content = content
                response['Content-Encoding'] = 'gzip'
            response['ETag'] = md5.new(content).hexdigest()
            response['Content-Length'] = '%d' % len(content)
            response['Last-Modified'] = datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
            cache.set(request._cache_middleware_key, response, settings.CACHE_MIDDLEWARE_SECONDS)
        return response